Repository navigation
PP-11: convert, named-axis OME-TIFF copies of images with axis errors - #124
Merged
Merged
Conversation
The exception path of design v0.4 §5: convert_experiment (conversion) writes a copy tree outside the experiment, every image with an axis error or unnamed axes as a named-axis OME-TIFF with the confirmed roles (copy_axes, ome_tiff_copy: planes read by the run's reader, in blocks over the memory limit), every other image copied byte for byte, .partial files moved into place together, the conversion record written last (conversion_record). A run on the copy tree records conversion_record_sha256 and warns when a source or a copy changed. CLI convert; the axis messages name it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Tests (test_convert.py, test_convert_run.py): bitwise-equal planes for plain stacks, ImageJ stacks and hyperstacks, Y/X-swapped headers, channels, Z/T, X before Y, RGB, every pixel type and planes read in blocks; sizes by role; reuse, refusals, failure clean-up, partial files, the lock; the run on the copies (named axes, every row recomputed and equal, record SHA, warnings); OME-Zarr and masks; the CLI. The record keeps the confirmed experiment mapping. Contract §17, plan §10 and question 59 (open choices for the owner), user guide, README, AGENTS.md and the notebook's common problems. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
guijacquemet
marked this pull request as ready for review
November 2, 2026 13:49
conversion.py was at 579 of the 600 lines §9 allows; the review's fixes need room. The error builders move unchanged (one per refusal), and the layer is declared. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
A ValueError from tifffile (for example a file too large for a classic TIFF) or an interruption left a .partial file and the folders made behind, and a failure while moving the copies into place left the copies moved so far without a record. Now every .partial file, every copy moved (each where nothing was before) and every folder made is removed on any exception; read and write failures (ValueError included) are conversion_failed, anything else is re-raised. Regression tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
A plane that cannot be read within the memory limit, not even in blocks, was found only while writing, after the copies before it were written (and then removed). The planes of the images to convert are now checked from their headers as the run checks them (check_planes) before anything is written: plane_over_memory_limit and unsupported_layout stop the conversion, planes_read_in_blocks is reported. A memory_limit that is not a whole number of bytes above 0, or a missing_as_singleton that is not True or False, is refused (ValueError) before anything is read. Criteria of the obsolete issues #76 and #80. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
The copy was written as a classic TIFF whenever its pixels took at most 4 GiB less 32 MiB. With many small pages (about 170 bytes of directory each), the file can pass 4 GiB while the pixels do not, and tifffile then refuses to write it. needs_bigtiff adds 1 KiB per page. Regression test at the old threshold with 2**18 pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…decision) Owner decision of 2 November 2026 (plan question 59, (1)): when a Y or X is relabelled as the header's other one of Y and X, and the header gives X and Y exactly equal (same value and unit), the size is carried to the copy, since nothing can be exchanged; otherwise both stay unknown, with the reason in the record. Never exchanged, never guessed; other relabelled axes (a z-step on an axis confirmed as T) stay unknown. Tests: OME equal, unequal, other unit, one size only; ImageJ hyperstack equal/unequal. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
A rerun reused a copy when its source, roles and copy axes were unchanged, whatever pixel sizes the record gave it, so copies written under another rule (for example before the owner's decision on equal X and Y sizes) could be mixed with new ones. same_plan now also compares the source axes, the copy's pixel sizes and which sizes are unknown; a copy that differs is copy_differs, never reused nor overwritten. Criteria of the obsolete issues #92 and #96. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…owner decision) Owner decision of 2 November 2026 (plan question 59, (14)): a run whose manifest records conversion_record_sha256 is refused by RunInputs.open when the record in the experiment root is missing or changed since the run, as for calibration_sha256 and the design's SHA-256, with a message naming the record and what to do. Copies moved with their record are read with experiment_root=. Contract §9, §9.1 and §17; test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
not_enough_space had no test: one byte short is refused with nothing written, the exact size is accepted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…se convert' warning in convert A rerun that reused every copy rewrote the record with a new written_at, so its SHA-256 changed: the run on the copies, which recorded it, was then refused by RunInputs. The record is now kept when it would say the same (files, mapping, missing_as_singleton). convert also printed the axes_not_named warning advising to save named-axis copies with the convert command; like the axis errors, it is what the conversion corrects (each entry records it as its reason), so it is no longer reported there. Regression test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
…1b next) in the docs Plan question 59 is decided: (1) equal X and Y sizes kept through a Y/X swap, otherwise unknown; (14) RunInputs checks the conversion record; (15) the notebook card is PP-11b, now an entry of plan §10; the other interpretations accepted as built. The PP-11 entry lists the review's changes and the criteria of the obsolete convert_tiffs/zarr_export issues it meets. Contract §17, the user guide and AGENTS.md follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
… test write_text added carriage returns on Windows, so the restored record's SHA-256 differed and the test failed there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Contributor
Author
|
Independent review: merged as 8db1f4a (merge commit). Main (#125, v0.4.0rc3) was merged in first, without a rebase. Findings, each fixed with a regression test that fails on the previous head:
Owner decisions implemented:
Verified independently:
Gate:
§9 figures:
The obsolete issues #75, #76, #79, #80, #87, #88, #92 and #96 have their kept criteria met; the PR description lists them, so they can be closed. The branch was auto-deleted on merge and has been restored at de92031. Generated by Claude Code |
This was referenced Nov 2, 2026
[Sad Catapella B3][Scientific correctness] OME-Zarr pyramid mean corrupts uint64/int64 precision
#75
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PP-11 of the v0.4.0 plan: design v0.4 §5, "Conversion, the exception path". Version as main (0.4.0rc3, merged from main).
Summary (by module)
conversion(orchestration, new):convert_experiment(experiment_root, mapping, destination, exclude=, missing_as_singleton=, memory_limit=, progress=)andplan_conversion.check_planes), not enough free space, and settings that are not allowed (ValueError).axes_yx_swapped,axes_contradict_header,axes_x_before_yoraxes_not_namedare written as named-axis OME-TIFF with the user's roles. Every other image is copied byte for byte: images whose axes are already named and agree, OME-Zarr stores with their labels, and excluded images (never read).--to, required). It must be outside the experiment and must not contain it.<name>.partial, with the source's fingerprint checked before and after. All copies move into place together, then the record is written..partialfiles, the copies already moved and the folders made, so the folder is left as it was.copy_differs,copy_folder_not_empty) and never overwritten. When every copy is reused, the record is kept as it was.conversion_issues(pure, new): the conversion's messages.copy_axes(pure, new): each copy's axes (every role in source order, then Y and X), its shape and its OME metadata.ome_tiff_copy(io, new): an uncompressed OME-TIFF laid out withtifffile. Planes come from the run's owniter_planes, in blocks over the memory limit, each block written at its offset. BigTIFF is chosen when the pixels plus 1 KiB per page would pass 4 GiB less 32 MiB.conversion_record(io, new):.morphobricks-conversion.json, plusreview_conversionfor the run's warnings.conversion_record_sha256, the report has aconversionentry, and every row is recomputed. The warningsconversion_source_changedandcopy_changed_after_conversionare new; an unreadable record isconversion_record_unreadable.RunInputs.openrefuses a run whose conversion record has changed or disappeared since (owner decision).convert EXPERIMENT --to FOLDER --axes ROLESwith--exclude,--missing-as-singleton,--memory-limit,--remove-stale-lockand--log. It prints the nextcheckandruncommands.axis_check_summaryand the notebook's axis marks name theconvertcommand.Owner decisions (2 November 2026, plan question 59, all decided)
--tostays required.RunInputs.openchecksconversion_record_sha256.Criteria of the obsolete issues met here (so they can be closed)
check_planesruns before anything is written, and planes are read and written in blocks within the limit.memory_limitmust be an int above 0 and not a bool, andmissing_as_singletonmust be a bool. Both are checked before any I/O.Pixel-exactness evidence
The tests compare every plane of each copy with the original read under the confirmed roles, byte for byte, with dtype and shape. Cases: plain stacks, ImageJ slice stacks and hyperstacks, Y/X-swapped OME, multi-channel, CYX confirmed as ZYX, X before Y, RGB swapped, int8/uint16/int32/float32 with NaN and ±inf, big-endian float, block reads, BigTIFF.
The independent review re-checked the following, all byte-identical:
The copies pass
checkwithaxes_from: "header"and no errors, and the originals' SHA-256 are unchanged.End to end: a plain multi-channel float experiment with a design was run, converted, then run on the copies with the same output folder.
plane_histograms.csvis byte-identical, andRunInputsreads the copies.256 MiB uint16 (8 × 4096 × 4096), Y/X swapped:
Tests
ruff check,ruff format --check,tools/check.py --rules, pytest with.[dev,notebook,zarr], Python 3.12): 1750 passed, 1 skipped. The skip istest_discovery.py:665, expected when running as root. Main has 1691 passed, 1 skipped.tests/test_convert.pyandtests/test_convert_run.py. The review's regression tests fail on the PR's previous head.§9 figures
conversion587,conversion_record386,copy_axes247,ome_tiff_copy152,cli_convert141,conversion_issues136.cli_convert.add_convert_parser, 48 executable lines.conversion._write_all, 8.convert_experiment, 7.# noqacount is unchanged (49).Not verified: real microscopy files (simulated data only).
https://claude.ai/code/session_01G3qp9AxjpGPw9xfWAUJNqc
Generated by Claude Code